// use std::io::Error as IoError;
use std::path::Path;
use std::sync::Arc;

use async_std::{fs::OpenOptions, io};
// use serde_json::json;
use crate::Request;
use tempfile::TempDir;
use tide::prelude::*;
use tide::{Body, Response, StatusCode};

use tide::log::debug;

#[derive(Clone)]
pub struct TempDirState {
    tempdir: Arc<TempDir>,
}

impl TempDirState {
    #[allow(dead_code)]
    pub fn try_new() -> anyhow::Result<Self> {
        Ok(Self {
            tempdir: Arc::new(tempfile::tempdir()?),
        })
    }

    #[allow(dead_code)]
    pub fn path(&self) -> &Path {
        self.tempdir.path()
    }
}
pub struct TempDirMiddleware {
    tempdir: Arc<TempDir>,
}

impl TempDirMiddleware {
    pub fn new() -> anyhow::Result<Self> {
        Ok(Self {
            tempdir: Arc::new(tempfile::tempdir()?),
        })
    }

    #[allow(dead_code)]
    pub fn path(&self) -> &Path {
        self.tempdir.path()
    }
}

use tide::{Middleware, Next, Result};

#[tide::utils::async_trait]
impl<State: Clone + Send + Sync + 'static> Middleware<State> for TempDirMiddleware {
    async fn handle(&self, mut req: tide::Request<State>, next: Next<'_, State>) -> Result {
        tide::log::trace!("request :{:?}", self.tempdir.clone());
        req.set_ext(self.tempdir.clone());
        // let mut res = next.run(req).await;
        Ok(next.run(req).await)
    }
}

pub async fn upload(req: Request) -> tide::Result {
    let path: String = req.param("file")?;
    let fs_path = req.state().tempdir.path().join(path);

    let file = OpenOptions::new()
        .create(true)
        .write(true)
        .open(&fs_path)
        .await?;

    let bytes_written = io::copy(req, file).await?;

    tide::log::info!("file written", {
        bytes: bytes_written,
        path: fs_path.canonicalize()?.to_str()
    });

    Ok(json!({ "bytes": bytes_written }).into())
}

pub async fn upload_file(req: Request) -> tide::Result {
    let path: String = req.param("file")?;
    let fs_path = req.state().tempdir.path().join(path);

    debug!("{:?}", fs_path);

    if let Ok(body) = Body::from_file(fs_path).await {
        Ok(body.into())
    } else {
        Ok(Response::new(StatusCode::NotFound))
    }
}
